﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace L4View.Infrastructure
{
    public class MessageQueue : IMessageQueue
    {
        private readonly int _capacity;

        private readonly IForwardConditionCheck _forwardConditionCheck;

        private readonly Queue<IMessage> _queue;

        private IMessageHandler _messageSink;

        private readonly object _syncObject = new object();

        public MessageQueue(int capacity, IForwardConditionCheck forwardConditionCheck)
        {
            if (capacity <= 1)
                throw new ArgumentException("Must be greater than 1.", "capacity");

            _capacity = capacity;
            _forwardConditionCheck = forwardConditionCheck;
            _queue = new Queue<IMessage>(capacity);
        }

        public MessageQueue(int capacity)
            : this( capacity, new ForwardConditionCheck() )
        {
        }

        public int Capacity
        {
            get { return _capacity; }
        }

        public int MessageCount
        {
            get { return _queue.Count; }
        }

        public void RegisterSink(IMessageHandler sink)
        {
            lock (_syncObject)
            {
                _messageSink = sink;
                
                ForwardMessages();
            }
        }

        public void Clear()
        {
            lock (_syncObject)
            {
                _queue.Clear();
            }
        }

        public void Transfer(bool stop)
        {
        }

        public void PutMessage(IMessage message)
        {
            lock (_syncObject)
            {
                if (message == null)
                    throw new ArgumentNullException("message");

                if (_queue.Contains(message))
                    throw new ArgumentException("Message already added.", "message");

                if (_queue.Count == _capacity)
                    _queue.Dequeue();

                _queue.Enqueue(message);

                ForwardMessages();
            }
        }

        protected void ForwardMessages()
        {
            bool forward = CanForward();

            if (forward && _messageSink != null)
            {
                _messageSink.Transfer(false);

                while (_queue.Count > 0)
                    _messageSink.PutMessage(_queue.Dequeue());

                _messageSink.Transfer(true);
            }
        }

        private bool CanForward()
        {
            if (_forwardConditionCheck == null)
                throw new NullReferenceException("ForwardConditionCheck MUST BE SET!");

            return _forwardConditionCheck.CanForward(this);
        }

        #region Nested Classes
        
        class ForwardConditionCheck : IForwardConditionCheck
        {
            public bool CanForward(IMessageQueue messageQueue)
            {
                return !(messageQueue.MessageCount < (messageQueue.Capacity / 3));
            }
        } 

        #endregion
    }
}
